﻿using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;

namespace O1.Kernel
{
    [ContractVerification(true)]
    public class Edge 
    {
        public const int Size = Linkage.Size + CellAddress.Size + (Sz.CLR.Int32 * 2);

        public Edge()
        {
        }

        public Edge(
            Linkage startVertexLinkage,
            CellAddress endVertex,
            int firstPropertyId,
            int edgeType)
        {
            this.Linkage = startVertexLinkage;
            this.EndVertex = endVertex;
            this.FirstPropertyId = firstPropertyId;
            this.EdgeType = edgeType;
        }

        public Linkage Linkage { get; private set; }

        public CellAddress EndVertex { get; private set; }

        public int FirstPropertyId { get; private set; }

        public int EdgeType { get; private set; }

        internal static unsafe Edge Read(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                return new Edge(
                    *(Linkage*)p,
                    *(CellAddress*)(p + Linkage.Size),
                    *(int*)(p + Linkage.Size + CellAddress.Size),
                    *(int*)(p + Linkage.Size + CellAddress.Size + Sz.CLR.Int32));
            }
        }

        internal unsafe void Load(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                this.Linkage = *(Linkage*)p;
                this.EndVertex = *(CellAddress*)(p + Linkage.Size);
                this.FirstPropertyId = *(int*)(p + Linkage.Size + CellAddress.Size);
                this.EdgeType = *(int*)(p + Linkage.Size + CellAddress.Size + Sz.CLR.Int32);
            }
        }

        internal unsafe void Write(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                *(Linkage*)p = this.Linkage;
                *(CellAddress*)(p + Linkage.Size) = this.EndVertex;
                *(int*)(p + Linkage.Size + CellAddress.Size) = this.FirstPropertyId;
                *(int*)(p + Linkage.Size + CellAddress.Size + Sz.CLR.Int32) = this.EdgeType;
            }
        }
    }
}
